使用矢量图形节点
Dora SSR 基于 nanovg 库,提供了一套强大的矢量图形绘制功能。本教程将引导您在 Dora SSR 中使用这些矢量图形功能。您将学习如何创建矢量图形节点、渲染形状、应用变换,以及加载和显示 SVG 文件。
1. 简介
矢量图形允许使用数学方程而非像素网格来创建可缩放、与分辨率无关的图像。Dora SSR 的矢量图形功能基于 nanovg
库,提供 了基础的类似于 HTML5 Canvas 的矢量绘图 API。并且可以和 Dora SSR 的场景节点系统进行集成使用。
在本教程中,我们将探索如何使用 Dora SSR 的矢量图形 API 来:
- 创建矢量图形节点(
VGNode
) - 直接在屏幕上绘制矢量图形
- 渲染形状和路径
- 应用变换
- 加载和显示 SVG 文件
2. 使用 VGNode
VGNode
是 Dora SSR 中用于将矢量图形静态渲染到一张纹理上,并挂载到游戏场景中展示的节点。所以它的内部包含了一张用于绘制矢量图形的帧缓冲纹理,并且本身是一个场景节点对象。要创建一个 VGNode
,您需要指定其尺寸,以及可选的缩放比例和边缘抗锯齿系数。
2.1 创建节点对象
- Lua
- Teal
- TypeScript
- YueScript
local VGNode <const> = require("VGNode")
-- 创建一个宽800,高600,默认缩放比例和边缘抗锯齿的 VGNode
local node = VGNode(
800, -- 帧缓冲纹理的宽度
600, -- 帧缓冲纹理的高度
1.0, -- 可选渲染单位的缩放比例。默认值为 `1.0`
1 -- 可选的边缘抗锯齿系数。默认值为 `1`
)
local VGNode <const> = require("VGNode")
local node = VGNode(
800, -- 帧缓冲纹理的宽度
600, -- 帧缓冲纹理的高度
1.0, -- 可选渲染单位的缩放比例。默认值为 `1.0`
1 -- 可选的边缘抗锯齿系数。默认值为 `1`
)
import { VGNode } from "Dora";
const node = VGNode(
800, // 帧缓冲纹理的宽度
600, // 帧缓冲纹理的高度
1.0, // 可选渲染单位的缩放比例。默认值为 `1.0`
1 // 可选的边缘抗锯齿系数。默认值为 `1`
);
_ENV = Dora
node = VGNode(
800 -- 帧缓冲纹理的宽度
600 -- 帧缓冲纹理的高度
1.0 -- 可选渲染单位的缩放比例。默认值为 `1.0`
1 -- 可选的边缘抗锯齿系数。默认值为 `1`
)
这将创建一个 VGNode
实例,我们将使用它把矢量图形渲染到自带的帧缓冲纹理上。
2.2 渲染矢量图形
要渲染矢量图形,使用 VGNode
实例的 render
方法。您需要传递一个包含使用 nvg
模块的绘图命令的函数(闭包)。这些绘图命令会将图形绘制到 VGNode
的帧缓冲纹理上。
nvg
模块提供了绘制矩形、圆形、线条和路径等基本形状的函数。您还可以使用颜色和画笔来自定义形状的外观。
示例:绘制矩形
- Lua
- Teal
- TypeScript
- YueScript
local VGNode <const> = require("VGNode")
local nvg <const> = require("nvg")
local Color <const> = require("Color")
local node = VGNode(200, 150)
node:render(function()
nvg.BeginPath() -- 开始新路径
nvg.Rect(0, 0, 200, 150) -- x, y, 宽度, 高度
nvg.FillColor(Color(255, 0, 0, 255)) -- 红色
nvg.Fill()
end)
local VGNode <const> = require("VGNode")
local nvg <const> = require("nvg")
local Color <const> = require("Color")
local node = VGNode(200, 150)
node:render(function()
nvg.BeginPath() -- 开始新路径
nvg.Rect(0, 0, 200, 150) -- x, y, 宽度, 高度
nvg.FillColor(Color(255, 0, 0, 255)) -- 红色
nvg.Fill()
end)
import { VGNode, Color } from "Dora";
import * as nvg from "nvg";
const node = VGNode(200, 150);
node.render(() => {
nvg.BeginPath(); // 开始新路径
nvg.Rect(0, 0, 200, 150); // x, y, 宽度, 高度
nvg.FillColor(Color(255, 0, 0, 255)); // 红色
nvg.Fill();
});
_ENV = Dora
with VGNode 200, 150
\render ->
nvg.BeginPath! -- 开始新路径
nvg.Rect 0, 0, 200, 150 -- x, y, 宽度, 高度
nvg.FillColor Color 255, 0, 0, 255 -- 红色
nvg.Fill!
上述代码绘制了一个宽 200、高 150 的红色矩形。并且 render
方法只会在第一次调用时执行,之后会自动缓存渲染结果进行复用。
使用 VGNode
渲染矢量图形时,坐标系原点在节点纹理的左上角,x 轴向右,y 轴向下。以纹理像素乘以缩放值为图形计算单位。
示例:绘制圆形
- Lua
- Teal
- TypeScript
- YueScript
local VGNode <const> = require("VGNode")
local nvg <const> = require("nvg")
local Color <const> = require("Color")
local node = VGNode(100, 100)
node:render(function()
nvg.BeginPath()
nvg.Circle(50, 50, 50) -- 中心 x, y 和半径
nvg.FillColor(Color(255, 255, 0, 255)) -- 黄色
nvg.Fill()
end)
local VGNode <const> = require("VGNode")
local nvg <const> = require("nvg")
local Color <const> = require("Color")
local node = VGNode(100, 100)
node:render(function()
nvg.BeginPath()
nvg.Circle(50, 50, 50) -- 中心 x, y 和半径
nvg.FillColor(Color(255, 255, 0, 255)) -- 黄色
nvg.Fill()
end)
import { VGNode, Color } from "Dora";
import * as nvg from "nvg";
const node = VGNode(100, 100);
node.render(() => {
nvg.BeginPath();
nvg.Circle(50, 50, 50); // 中心 x, y 和半径
nvg.FillColor(Color(255, 255, 0, 255)); // 黄色
nvg.Fill();
});
_ENV = Dora
with VGNode 100, 100
\render ->
nvg.BeginPath! -- 开始新路径
nvg.Circle 50, 50, 50 -- 中心 x, y 和半径
nvg.FillColor Color 255, 255, 0, 255 -- 黄色
nvg.Fill!
该代码绘制了一个半径为 50 的黄色圆形。同样,render
方法只会执行一次,并缓存渲染结果进行复用。
3. 直接绘制矢量图形到屏幕
除了使用 VGNode,Dora SSR 还支持直接在屏幕缓冲区的最上层绘制矢量图形。只需在每一帧调用 nvg 模块的绘图函数,即可直接将矢量图形绘制到屏幕上,无需借助 VGNode。
- Lua
- Teal
- TypeScript
- YueScript
local nvg <const> = require("nvg")
local Color <const> = require("Color")
local threadLoop <const> = require("threadLoop")
threadLoop(function()
nvg.BeginPath()
nvg.Rect(100, 100, 200, 150)
nvg.FillColor(Color(255, 0, 0, 255))
nvg.Fill()
end)
local nvg <const> = require("nvg")
local Color <const> = require("Color")
local threadLoop <const> = require("threadLoop")
threadLoop(function(): boolean
nvg.BeginPath()
nvg.Rect(100, 100, 200, 150)
nvg.FillColor(Color(255, 0, 0, 255))
nvg.Fill()
return false
end)
import { Color, threadLoop } from "Dora";
import * as nvg from "nvg";
threadLoop(() => {
nvg.BeginPath();
nvg.Rect(100, 100, 200, 150);
nvg.FillColor(Color(255, 0, 0, 255));
nvg.Fill();
return false;
});
_ENV = Dora
threadLoop ->
nvg.BeginPath! -- 开始新路径
nvg.Rect 100, 100, 200, 150
nvg.FillColor Color 255, 0, 0, 255 -- 红色
nvg.Fill!
上述代码在屏幕上绘制了一个红色矩形。请注意,这种方式会直接绘制到屏幕缓冲区,并覆盖下面的游戏场景。比较适合用于绘制游戏的 HUD 界面、显示调试信息等。
使用这种绘制方式时,坐标系原点在屏幕左上角,x 轴向右,y 轴向下。在屏幕的可见区,坐标横轴的最大值为 App.visualSize.width
,纵轴的最大值为 App.visualSize.height
。
3.1 结合游戏场景节点和矢量图形
您可以使用 nvg.ApplyTransform(node)
函数将一个场景节点的变换应用到要绘制的矢量图形上。这样您可以在游戏场景中,用更便捷的方式,通过控制节点的变换来改变矢量图形的位置、旋转和缩放。
- Lua
- Teal
- TypeScript
- YueScript
local nvg <const> = require("nvg")
local Color <const> = require("Color")
local Node <const> = require("Node")
local Size <const> = require("Size")
local Scale <const> = require("Scale")
local node = Node()
node.size = Size(200, 150)
node:schedule(function()
nvg.ApplyTransform(node)
nvg.BeginPath()
nvg.Rect(0, 0, 200, 150)
nvg.FillColor(Color(255, 0, 0, 255))
nvg.Fill()
end)
node.x = 50
node.y = 50
node.angle = 45
node:perform(Scale(0.5, 0, 1))